package com.sharding.algorithm;

import com.google.common.collect.Range;
import com.sharding.utils.DateTimeUtils;
import com.sharding.utils.anno.DateTimeFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Desc: 自定义分片算法
 * Date:    2023/2/26
 *
 * @author WanYangWei
 */
@Component
@Slf4j
public class MyTableShardingAlgorithm implements StandardShardingAlgorithm<Timestamp> {
    private final String tablePrefix = "goods_";
    private static final String isoDateTimePattern = "yyyy-MM-dd HH:mm:ss";

    /**
     * 精准分片
     *
     * @param tableNames           : 配置中定义的表名集合 goods_$->{0..1} 即goods_0 ,goods_1
     *                             分库时为定义的分库集合
     * @param preciseShardingValue :对应表中的分片字段-对应的值 (如 createTime 2023-02-01 00:00:00)
     * @return 分片后对应的表名
     */
    @Override
    public String doSharding(Collection<String> tableNames, PreciseShardingValue<Timestamp> preciseShardingValue) {
        if (preciseShardingValue != null) {
            String shardingValue = null;
            try {
                //获取分片键字段值
                Timestamp timestamp = preciseShardingValue.getValue();
                shardingValue = DateTimeUtils.formatTimestamp(timestamp);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
//            String shardingValue = (String)preciseShardingValue.getValue();
            //获取表名后缀
            String tableSuffix = getTableSuffix(shardingValue);
            //判断定义的表名后缀是否包含改后缀
            for (String tableName : tableNames) {
                if (tableName.endsWith(tableSuffix)) {
                    return tableName;
                }
            }
            throw new UnsupportedOperationException("没有匹配到可用的表");
        } else {
            throw new UnsupportedOperationException("分片列表为空");
        }
    }

    /**
     * tableNames:配置文件中定义的表名称集合
     * rangeShardingValue：insert,select,update,delate时，createTime 范围查询字段值。比如 between 2023-01-01 00:00:00  and 2023-02-02 00:00:00，存储的就是 2023-01-01 00:00:00  and 2023-02-02 00:00:00
     * 函数返回值：返回函数值rangeShardingValue 年月对应的表名集合比如 between 2023-01-01 00:00:00  and 2023-02-02 00:00:00 ，根据这时间段年份，得到返回的集合是 goods_2301,goods_2302
     */
    @Override
    public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<Timestamp> rangeShardingValue) {
        Collection<String> collect = new ArrayList<>();
        try {
            if (rangeShardingValue != null) {
                //获得范围区间值
                Range<Timestamp> valueRange = rangeShardingValue.getValueRange();
                //获得返回区间值下限
                String slowerEndpointDate = String.valueOf(valueRange.hasLowerBound() ? valueRange.lowerEndpoint() : "");
                //获得范围区间值上限
                String supperEndpointDate = String.valueOf(valueRange.hasUpperBound() ? valueRange.upperEndpoint() : "");


                //开始时间
                String startTime = getTableSuffix(slowerEndpointDate);
                //结束时间
                String endTime = getTableSuffix(supperEndpointDate);
                //开始时间大于结束时间
                if (StringUtils.compare(startTime, endTime) > 0) {
                    String tmp = null;
                    tmp = startTime;
                    startTime = endTime;
                    endTime = tmp;
                }

                //根据上下限范围，循环取值判断对应的表名称，返回符合条件的表名称集合
                //todo 不跨年 2301 -> 2312
                for (int i = Integer.parseInt(startTime); i <= Integer.parseInt(endTime); i++) {
//                tableNames.stream().filter(s->s.endsWith(i)).c
                    for (String tableName : tableNames) {
                        if (tableName.endsWith(String.valueOf(i))) {
                            if (!collect.contains(tableName)) {
                                collect.add(tableName);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("get range time has error", e);
        }
        return collect;
    }

    @Override
    public void init() {

    }

    @Override
    public String getType() {
        return null;
    }

    /**
     * @param dateTimeStr 格式为 YYYY-MM-DD HH:mm:ss
     * @return 年月格式 : 2023-02-01 00:00:00 -> 2302
     */
    private String getTableSuffix(String dateTimeStr) {
        SimpleDateFormat dateTimeFormat = new SimpleDateFormat(isoDateTimePattern);
        try {
            //校验格式是否正确
            dateTimeFormat.parse(dateTimeStr);
        } catch (Exception e) {
            log.error("日期格式错误:{}不符合{}的格式", dateTimeStr, isoDateTimePattern);
            dateTimeStr = DateTimeUtils.getDateTimeStr();
            log.info("使用当前日期处理:{}", dateTimeStr);
        }
        return dateTimeStr.replaceAll("\\D", "").substring(2, 6);
    }
}
